package com.xframe.xdal.core.autofill;


import com.xframe.xdal.core.autofill.proc.*;
import com.xframe.xdal.core.constant.AutoFillType;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;

/**
 * 功能描述: 创建自动填充处理器
 *
 * @author xufeng
 * @version 1.0
 * @date 2022-09-26
 */
public class AutoFillFactory {

    static Map<String, IAutoProc> container = new HashMap<>();


    static {
        container.put(AutoFillType.Snowflake.toString(), new SnowflakeAutoProc());
        container.put(AutoFillType.UUID.toString(), new UUIDAutoProc());
        container.put(AutoFillType.MD5.toString(),new MD5AutoProc());
        container.put(AutoFillType.ENCRYPT.toString(),new EncryptProc());
        container.put(AutoFillType.DECRYPT.toString(),new DecryptProc());
        container.put(AutoFillType.RandomValue.toString(),new RandomDefVal());
        container.put(AutoFillType.Value.toString(),new ValueAutoProc());
        container.put(AutoFillType.BILLGENERATOR.toString(),new BillNoCreateProc());
        container.put(AutoFillType.ROLEVAL.toString(),new RoleValCreateProc());
    }

    /**
     * 自动填充处理器工厂
     *
     * @param autoFill  字段上的注解
     * @param fieldType 字段类型
     * @return IAutoProc
     */
    public static IAutoProc create(IAutoFillBean autoFill, Class<?> fieldType) {
        AutoFillType autoFillType = autoFill.getAutoFillType();
        IAutoProc autoProc = null;
        switch (autoFillType) {
            case Snowflake:
                autoProc = container.get(AutoFillType.Snowflake.toString());
                break;
            case UUID:
                autoProc = container.get(AutoFillType.UUID.toString());
                break;
            case Value:
                autoProc = container.get(AutoFillType.Value.toString());
                break;
            case Custom:
                autoProc = createHelp(autoFill.getType());
                break;
            case ENCRYPT:
                autoProc = container.get(AutoFillType.ENCRYPT.toString());
                break;
            case DECRYPT:
                autoProc = container.get(AutoFillType.DECRYPT.toString());
                break;
            case MD5:
                autoProc = container.get(AutoFillType.MD5.toString());
                break;
            case RandomValue:
                autoProc = container.get(AutoFillType.RandomValue.toString());
                break;
            case BILLGENERATOR:
                autoProc = container.get(AutoFillType.BILLGENERATOR.toString());
                break;
            case ROLEVAL:
                autoProc = container.get(AutoFillType.ROLEVAL.toString());
                break;
        }
        return autoProc;
    }


    @SuppressWarnings("unchecked")
    private static IAutoProc createHelp(String typeName) {
        if (container.containsKey(typeName)) {
            return container.get(typeName);
        }
        try {
            Class<?> cls = Class.forName(typeName);
            Object autoProc = cls.newInstance();

            if (autoProc instanceof IAutoProc) {
                container.put(typeName, (IAutoProc) autoProc);
                return container.get(typeName);
            }
            return null;
        } catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) {
            e.printStackTrace();
            return null;
        }
    }

    @SuppressWarnings("unchecked")
    private static IAutoProc createHelp(String typeName, Class<?> typeParameter) {
        if (container.containsKey(typeName)) {
            return container.get(typeName);
        }
        try {
            Class<?> cls = Class.forName(typeName);
            Constructor[] constructors = cls.getDeclaredConstructors();
            //查找只有一个参数的构造，后期可以根据实际情况进行扩展处理
            Optional<Constructor> constructor = Arrays.stream(constructors).filter(item -> item.getParameterCount() == 1).findFirst();

            Object autoProc;
            //如果未找到有参数的构造，则使用默认的构造
            if (constructor.equals(Optional.empty())) {
                autoProc = cls.newInstance();
            } else {
                autoProc = constructor.get().newInstance(typeParameter);
            }

            if (autoProc instanceof IAutoProc) {
                container.put(typeName, (IAutoProc) autoProc);
                return container.get(typeName);
            }
            return null;
        } catch (ClassNotFoundException | InstantiationException | IllegalAccessException |
                 InvocationTargetException e) {
            e.printStackTrace();
            return null;
        }
    }
}
